#include "mktablemodel.h"

#include "mkcore/mkcore.h"
#include "mkcore/mksettings.h"

#include <QFile>
#include <QDateTime>

#include <QDebug>

const quint32 MagicNumber = 0xff87dc56;
const quint16 FormatNumber = 0x0100;

MKTableModel::MKTableModel(QObject *parent) : QAbstractTableModel(parent)
{
}

Qt::ItemFlags MKTableModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags theFlags = QAbstractTableModel::flags(index);
	if (index.isValid())
	{
		theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEditable |
					Qt::ItemIsEnabled;
	}
	return theFlags;
}

QVariant MKTableModel::data(const QModelIndex &index, int role) const
{
	bool isOutRange =	index.row() < 0 || index.row() > mRecords.count() ||
						index.column() < 0 || index.column() > static_cast<int>(C_Size);
	if (!index.isValid() || isOutRange)
	{
		return QVariant();
	}

	const RecordItem &record = mRecords.at(index.row());

	if (role == Qt::EditRole)
	{
		switch (index.column())
		{
			case C_Type: return record.type;
			case C_Category: return record.category;
			case C_Date: return record.date;
			case C_Cost: return record.cost;
			default: Q_ASSERT(false);
		}
	}

	if (role == Qt::DisplayRole)
	{
		MKSettings *settings = MKCore::instance()->settings();
		switch (index.column())
		{
			case C_Type: return settings->typeRuName(record.type);
			case C_Category: return settings->categoryRuName(record.type, record.category);
			case C_Date: return QDateTime::fromTime_t(record.date).toString("dd.MM.yyyy");
			case C_Cost: return record.cost;
			default: Q_ASSERT(false);
		}
	}

	return QVariant();
}

QVariant MKTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (role != Qt::DisplayRole)
	{
		return QVariant();
	}

	if (orientation == Qt::Horizontal)
	{
		switch (section)
		{
			case C_Type: return "Тип";
			case C_Category: return "Категория";
			case C_Date: return "Дата";
			case C_Cost: return "Стоимость";
			default: Q_ASSERT(false);
		}
	}

	return section + 1;
}

int MKTableModel::rowCount(const QModelIndex &index) const
{
	return index.isValid() ? 0 : mRecords.count();
}

int MKTableModel::columnCount(const QModelIndex &index) const
{
	return index.isValid() ? 0 : static_cast<int>(C_Size);
}

bool MKTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
	bool isOutRange =	index.row() < 0 || index.row() > mRecords.count() ||
						index.column() < 0 || index.column() > static_cast<int>(C_Size);
	if (!index.isValid() || isOutRange || (role != Qt::EditRole))
	{
		return false;
	}

	RecordItem &record = mRecords[index.row()];
	MKSettings *settings = MKCore::instance()->settings();
	switch (index.column())
	{
		case C_Type:
		{
			bool ok;
			quint8 type = static_cast<quint8>(value.toUInt(&ok));
			if (!ok)
			{
				return false;
			}
			if (record.type == type)
			{
				return false;
			}
			record.type = type;
			record.category = settings->categoriesByType(type).first();

			break;
		};
		case C_Category:
		{
			bool ok;
			quint8 category = static_cast<quint8>(value.toUInt(&ok));
			if (!ok)
			{
				return false;
			}
			if (record.category == category)
			{
				return false;
			}
			record.category = category;
			break;
		};
		case C_Date:
		{
			bool ok;
			quint32 date = static_cast<quint32>(value.toUInt(&ok));
			if (!ok)
			{
				return false;
			}
			if (record.date == date)
			{
				return false;
			}
			record.date = date;
			break;
		};
		case C_Cost:
		{
			bool ok;
			quint32 cost = static_cast<quint32>(value.toUInt(&ok));
			if (!ok)
			{
				return false;
			}
			if (record.cost == cost)
			{
				return false;
			}
			record.cost = cost;
			break;
		};
		default: Q_ASSERT(false);
	}
	emit dataChanged(index, index);
	return true;
}

bool MKTableModel::setHeaderData(int, Qt::Orientation, const QVariant &, int)
{
	return false;
}

bool MKTableModel::insertRows(int row, int count, const QModelIndex &)
{
	beginInsertRows(QModelIndex(), row, row + count - 1);
	for (int i = 0; i < count; ++i)
	{
		quint32 recordId = lastRecordId();
		mRecords.insert(row, RecordItem(recordId));
	}
	endInsertRows();
	return true;
}

void MKTableModel::reset()
{
	if (mRecords.count() == 0)
	{
		return;
	}
	beginInsertRows(QModelIndex(), 0, mRecords.count() - 1);
	endInsertRows();

	QModelIndex leftTop = index(0, C_Type);
	QModelIndex rightBottom = index(mRecords.count() - 1, C_Cost);
	emit dataChanged(leftTop, rightBottom);
}

void MKTableModel::clear()
{
	if (mRecords.count() == 0)
	{
		return;
	}
	beginRemoveRows(QModelIndex(), 0, mRecords.count() - 1);
	endRemoveRows();
	mRecords.clear();
}

qint32 MKTableModel::currentBalance()
{
	MKSettings *settings = MKCore::instance()->settings();
	qint32 balance = 0;
	QListIterator<RecordItem> i(mRecords);
	while (i.hasNext())
	{
		RecordItem record = i.next();
		quint8 type = record.type;
		quint32 cost = record.cost;
		qint8 sign = 1;
		if (settings->typeRuName(type) == "Расходы")
		{
			sign = -1;
		}
		balance += static_cast<qint32>(sign) * static_cast<qint32>(cost);
	}
	return balance;
}

QMap<quint8, quint32> MKTableModel::costsForTypes()
{
	QMap<quint8, quint32> result;
	QListIterator<RecordItem> i(mRecords);
	while (i.hasNext())
	{
		RecordItem record = i.next();
		quint8 type = record.type;
		quint32 cost = record.cost;

		result[type] += cost;
	}
	return result;
}

QMap<quint8, QMap<quint8, quint32> > MKTableModel::costsForCategories()
{
	QMap<quint8, QMap<quint8, quint32> > result;
	QListIterator<RecordItem> i(mRecords);
	while (i.hasNext())
	{
		RecordItem record = i.next();
		quint8 type = record.type;
		quint8 category = record.category;
		quint32 cost = record.cost;

		result[type][category] += cost;
	}
	return result;
}

QDataStream &operator <<(QDataStream &out, const RecordItem &record)
{
	out << static_cast<quint32>(record.id)
		<< static_cast<quint8>(record.type) << static_cast<quint32>(record.date)
		<< static_cast<quint8>(record.category) << static_cast<quint32>(record.cost);
	return out;
}


QDataStream &operator >>(QDataStream &in, RecordItem &record)
{
	quint32 id;
	quint8 type;
	quint32 date;
	quint8 category;
	quint32 cost;
	in >> id >> type >> date >> category >> cost;

	record.id = id;
	record.type = type;
	record.date = date;
	record.category = category;
	record.cost = cost;

	return in;
}


RecordItem::RecordItem(quint32 recordId)
{
	id = recordId;
	type = 0;
	category = 0;
	date = static_cast<quint32>(QDateTime::currentDateTime().toTime_t());
	cost = 0;
}

RecordItem::RecordItem()
{
	id = 0;
	type = 1;
	category = 1;
	date = 0;
	cost = 0;
}
